import { Destroyable, Event, extendClassProps, JsonValue, react, UniteChanged } from "xbsj-base";
import { ESVOptionUe, EngineObjsMap, ESObjectsManagerJsonType, ESVOption, ESVOptionCzm, ViewerObjsMap } from "../ESJTypes";
import { ESCameraViewCollection, ESSceneObject } from "../ESObjects";
import { ESViewer } from "../ESViewer";
import { SceneObjectsManager } from "./SceneObjectsManager";
import { ViewersManager } from "./ViewersManager";
import { DragStartDataManager } from "./base/DragStartDataManager";
import { PropUiTreeManager } from "./PropTrees";
import { SceneTree } from "./SceneTrees";

function register(viewers: unknown[]) {
    viewers.forEach((t: any) => {
        const type = t.type;
        const vmap = t.context.registerObjsMap as ViewerObjsMap;
        const emap = t.engineObjsContext.registerObjsMap as EngineObjsMap;
        //@ts-ignore
        (!window.ESSDK_EngineObjs) && (window.ESSDK_EngineObjs = {});
        //@ts-ignore
        (!window.ESSDK_ViewerObjs) && (window.ESSDK_ViewerObjs = {});
        //@ts-ignore
        window.ESSDK_EngineObjs[type] = emap;
        //@ts-ignore
        window.ESSDK_ViewerObjs[type] = vmap;
    });
}

export class ESObjectsManager extends Destroyable {
    static getSceneObjById = ESSceneObject.context.getSceneObjectById.bind(ESSceneObject.context);
    static getEnv = ESSceneObject.context.getEnv.bind(ESSceneObject.context);
    static setEnv = ESSceneObject.context.setEnv.bind(ESSceneObject.context);
    static get envs() { return ESSceneObject.context.environmentVariables };

    private _drgm = this.dv(new DragStartDataManager());
    get dragstartDataMananger() { return this._drgm; }

    private _sobjm = this.dv(new SceneObjectsManager());//所有对象
    private _vrm = this.dv(new ViewersManager(this._sobjm));//所有视口
    get sceneObjectsManager() { return this._sobjm; }
    get viewers() { return this._vrm.viewers; }
    getViewers() { return this._vrm.getViewers(); }

    private _propUiTreeManager = this.dv(new PropUiTreeManager(24));
    get propUiTreeManager() { return this._propUiTreeManager; }

    private _sceneTree = this.dv(new SceneTree('default', this.dragstartDataMananger, 24, this));
    get sceneTree() { return this._sceneTree; }

    private _activeViewer = this.dv(react<ESViewer | undefined>(undefined));
    get activeViewer() { return this._activeViewer.value; }
    set activeViewer(value: ESViewer | undefined) { this._activeViewer.value = value; }
    get activeViewerChanged() { return this._activeViewer.changed; }

    private _cmrvm = this.createSceneObjectFromClass(ESCameraViewCollection);
    private _cmrvmdon = this.d(() => this._cmrvm && this.destroySceneObject(this._cmrvm));
    get cameraViewsManager() { return this._cmrvm; }

    private _asset = {
        type: 'ESObjectsManager',
        version: '0.1.0',
        createdTime: '',
        modifiedTime: '',
        name: '未命名项目'
    }

    get json() {
        const createdTime = this._asset.createdTime || new Date().toISOString();
        const modifiedTime = new Date().toISOString();
        const name = this._asset.name || '未命名项目';
        const version = this._asset.version || '0.1.0';
        const asset = { type: 'ESObjectsManager', version, createdTime, modifiedTime, name }
        const sceneTree = this.sceneTree.json
        const viewCollection = this._cmrvm.views;
        //todo ysp  activeViewer
        // const viewers:JsonValue[] = [...this._viewers].map(viewer => viewer.json)
        // const lastView = this.activeViewer?.getCurrentCameraInfo()
        // return { asset, viewers, sceneTree, viewCollection, lastView };
        return { asset, viewCollection, sceneTree };
    }
    set json(value: ESObjectsManagerJsonType) {
        try {
            if (!value.asset) return;
            if (!value.asset.type || value.asset.type !== "ESObjectsManager") {
                console.warn("json装配失败! asset.type 不存在或者不是'ESObjectsManager'");
                return;
            }
            this._asset.createdTime = value.asset && value.asset.createdTime || new Date().toISOString();
            this._asset.modifiedTime = value.asset && value.asset.modifiedTime || '';
            this._asset.name = value.asset && value.asset.name || '未命名项目';
            this._asset.version = value.asset && value.asset.version || '0.1.0';
            value.sceneTree && (this.sceneTree.json = value.sceneTree);
            value.viewCollection && (this._cmrvm.views = value.viewCollection);

            //todo ysp
            // if (!value.viewers || !Array.isArray(value.viewers)) {
            //     console.warn('viewers is not an array or does not exist !');
            //     return;
            // };
            // if (this._viewers.size === 0 || value.viewers.length === 0) {
            //     console.warn('viewers is empty !');
            //     return;
            // };
            // this._viewers.forEach(v => {
            //     //@ts-ignore
            //     const viewer = value.viewers.find(v2 => v2.id === v.id);
            //     if (viewer) {
            //         //@ts-ignore
            //         v.json = viewer;
            //     }
            // })
        } catch (e) {
            console.error(`ESObjectsManager解析json数据时发生错误! error: ${e}`);
        }
    }
    private _viewerCreatedEvent = this.dv(new Event<[ESViewer]>());
    get viewerCreatedEvent() { return this._viewerCreatedEvent; }

    createViewer(option: ESVOption) { return this._vrm.createViewer(option); };
    destroyViewer<T extends ESViewer>(viewer: T) { return this._vrm.destroyViewer(viewer); };
    createCesiumViewer<T extends ESViewer>(params: ESVOptionCzm): T;
    createCesiumViewer<T extends ESViewer>(container: HTMLDivElement | string, options?: JsonValue, id?: string): T;
    createCesiumViewer<T extends ESViewer>(...args: any[]) {
        let opt: ESVOptionCzm | undefined = undefined;
        if (typeof args[0] === 'object' && !(args[0] instanceof HTMLDivElement)) {
            opt = { ...args[0], type: "ESCesiumViewer" };
        } else if (typeof args[0] === 'string' || args[0] instanceof HTMLDivElement) {
            opt = { container: args[0], options: args[1] ?? undefined, id: args[2] ?? undefined, type: "ESCesiumViewer" };
        }
        if (!opt) throw new Error("参数错误");
        return this.createViewer(opt) as T;
    }

    createUeViewer<T extends ESViewer>(params: ESVOptionUe): T;
    createUeViewer<T extends ESViewer>(container: HTMLDivElement | string, uri: string, app: string, token?: string, id?: string): T;
    createUeViewer<T extends ESViewer>(container: HTMLDivElement | string, ws: string, esmsg?: string, id?: string): T;
    createUeViewer<T extends ESViewer>(...args: any[]) {
        let opt: ESVOptionUe | undefined = undefined;
        if (typeof args[0] === 'object' && !(args[0] instanceof HTMLDivElement)) {
            opt = { ...args[0], type: "ESUeViewer" };
        } else if (typeof args[0] === 'string' || args[0] instanceof HTMLDivElement) {
            if (typeof args[1] === 'string' && args[1].startsWith("ws")) {
                opt = { type: "ESUeViewer", container: args[0], id: args[3] ?? undefined, options: { ws: args[1], esmsg: args[2] ?? undefined } };
            } else {
                opt = { type: "ESUeViewer", container: args[0], id: args[4] ?? undefined, options: { uri: args[1], app: args[2], token: args[3] ?? undefined } };
            }
        }
        if (!opt) throw new Error("参数错误");
        return this.createViewer(opt) as T;
    }

    constructor(...args: unknown[]) {
        super();
        register(args);
    }
    createSceneObject<T extends ESSceneObject>(sceneObjectType: string | (new (id?: string) => T), id?: string) {
        const sceneObject = this._sobjm.createSceneObject(sceneObjectType, id);
        return sceneObject;
    }
    createSceneObjectFromClass<T extends ESSceneObject>(sceneObjConstructor: new (id?: string) => T, id?: string) {
        const sceneObject = this._sobjm.createSceneObjectFromClass(sceneObjConstructor, id);
        return sceneObject;
    }
    createSceneObjectFromJson<T extends ESSceneObject>(sceneObjectJson: JsonValue & { type: string;[k: string]: any; }) {
        const sceneObject = this._sobjm.createSceneObjectFromJson(sceneObjectJson) as T | undefined;
        return sceneObject;
    }

    destroySceneObject<T extends ESSceneObject>(sceneObject: T) {
        const flag = this._sobjm.deleteSceneObject(sceneObject);
        sceneObject.destroy();
        return flag;
    }

    destroyAllSceneObjects() {
        const toDels = [...this._sobjm.sceneObjects];
        for (let sceneObject of toDels) {
            this.destroySceneObject(sceneObject);
        }
    }

}

export namespace ESObjectsManager {
    export const createDefaultProps = () => ({

    });
}
extendClassProps(ESObjectsManager.prototype, ESObjectsManager.createDefaultProps);
export interface ESObjectsManager extends UniteChanged<ReturnType<typeof ESObjectsManager.createDefaultProps>> { }
